package server

import (
	"encoding/json"
	"fmt"
	"log"
	"sync"
	"time"

	"github.com/aler9/gortsplib"
	"github.com/pion/rtcp"
	"github.com/pion/rtp"
	"github.com/pion/webrtc/v3"
)

type WeSocketWebrtcConnectionPush struct {
	*WeSocketWebrtcConnection

	receiver    map[string]*WeSocketWebrtcConnectionPull // 其它流引用本流
	receiverMux sync.RWMutex
}

func (tis *WeSocketWebrtcConnectionPush) Close() {
	log.Printf("关闭推流: %v %v", tis.Name, tis.UID)

	var pullList []*WeSocketWebrtcConnectionPull

	tis.receiverMux.Lock()
	for _, pull := range tis.receiver {
		pullList = append(pullList, pull)
	}
	tis.receiverMux.Unlock()

	for _, pull := range pullList {
		pull.Close()
	}

	if tis.pc != nil {
		_ = tis.pc.Close()
	}

	if tis.ws != nil {
		_ = tis.ws.Close()
	}
}

func (tis *WeSocketWebrtcConnectionPush) addReceiver(receiver *WeSocketWebrtcConnectionPull) {
	tis.receiverMux.Lock()
	tis.receiver[receiver.UID] = receiver
	tis.receiverMux.Unlock()
}

func (tis *WeSocketWebrtcConnectionPush) delReceiver(receiver *WeSocketWebrtcConnectionPull) {
	tis.receiverMux.Lock()
	delete(tis.receiver, receiver.UID)
	tis.receiverMux.Unlock()
}

func (tis *WeSocketWebrtcConnectionPush) OnRequest(req *JsonProtocol) {
	if tis == nil || req == nil {
		return
	}

	log.Printf("收到请求: %v", req.Method)

	switch req.Method {
	case Offer:
		_ = tis.onPushOffer(req.Offer.Offer)
	case Candidate:
	}
}

// onPushOffer 推流请求
func (tis *WeSocketWebrtcConnectionPush) onPushOffer(offer webrtc.SessionDescription) error {
	api := tis.engine.api

	peerConnection, err := api.NewPeerConnection(webrtc.Configuration{
		ICEServers: []webrtc.ICEServer{
			{
				URLs: []string{"stun:stun.l.google.com:19302"},
			},
		},
	})
	if err != nil {
		log.Println(err)
		return err
	}

	// 接收视频
	if _, err = peerConnection.AddTransceiverFromKind(webrtc.RTPCodecTypeVideo); err != nil {
		log.Println(err)
		return err
	}

	// 接收音频
	if _, err = peerConnection.AddTransceiverFromKind(webrtc.RTPCodecTypeAudio); err != nil {
		log.Println(err)
		return err
	}

	peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
		log.Printf("[ice state] Connection State has changed %s \n", connectionState.String())

		if connectionState == webrtc.ICEConnectionStateFailed {
			if closeErr := peerConnection.Close(); closeErr != nil {
				log.Println(closeErr)
			}
		}
	})

	peerConnection.OnICECandidate(func(candidate *webrtc.ICECandidate) {
		if candidate != nil {
			log.Printf("打洞数据 [ice] OnICECandidate %s \n", candidate.String())
		}
	})

	peerConnection.OnTrack(func(remoteTrack *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) {
		go func() {
			ticker := time.NewTicker(time.Second * 2)
			defer ticker.Stop()
			for range ticker.C {
				if rtcpErr := peerConnection.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: uint32(remoteTrack.SSRC())}}); rtcpErr != nil {
					fmt.Println(rtcpErr)
					break
				}
			}
		}()

		tis.onTrack(peerConnection, remoteTrack, receiver)
	})

	// Set the remoteWebrtc SessionDescription
	if err = peerConnection.SetRemoteDescription(offer); err != nil {
		log.Println(err)
		return err
	}

	// Create channel that is blocked until ICE Gathering is complete
	gatherComplete := webrtc.GatheringCompletePromise(peerConnection)

	// Create answer
	answer, err := peerConnection.CreateAnswer(nil)
	if err != nil {
		log.Println(err)
		return err
	}

	if err = peerConnection.SetLocalDescription(answer); err != nil {
		log.Println(err)
		return err
	}

	log.Println("wait PeerConnection complete")
	// Block until ICE Gathering is complete, disabling trickle ICE
	// we do this because we only can exchange one signaling message
	// in a production application you should exchange ICE Candidates via OnICECandidate
	<-gatherComplete

	log.Printf("创建push流成功 [%v]", tis.Name)

	tis.pc = peerConnection
	localSDP := peerConnection.LocalDescription()
	data, _ := json.Marshal(localSDP)
	log.Println(string(data))

	_ = tis.ws.WriteJSON(&JsonProtocol{
		Method: Answer,
		Answer: struct {
			Code   int                        `json:"code"`
			MSG    string                     `json:"msg"`
			Answer *webrtc.SessionDescription `json:"answer"`
		}{
			Code:   0,
			MSG:    "success",
			Answer: localSDP,
		},
	})

	return nil
}

// onTrack 接收rtp, 转发rtp
func (tis *WeSocketWebrtcConnectionPush) onTrack(peerConnection *webrtc.PeerConnection, remoteTrack *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) {
	// TODO: 接收流, 转发流
	var err error

	log.Printf("接收到流OnTrack %v", remoteTrack.Kind())

	if remoteTrack.Kind() == webrtc.RTPCodecTypeVideo {
		for _, rtpReceiver := range peerConnection.GetReceivers() {
			for _, remote := range rtpReceiver.Tracks() {
				codecParam := fmt.Sprintf("%v %v", remote.Codec().PayloadType, remote.Codec().MimeType)
				log.Printf("codec: %v, id: %v, streamID: %v, payloadType: %v, kind: %v, ssrc: %v",
					codecParam, remote.ID(), remote.StreamID(), remote.PayloadType(), remote.Kind(), remote.SSRC())
			}
		}
	}

	var (
		cli   = gortsplib.Client{}
		cliOK = false
	)

	if remoteTrack.Kind() == webrtc.RTPCodecTypeVideo {
		var (
			rtspURL = fmt.Sprintf("rtsp://127.0.0.1:%v/%v", tis.engine.rtspPort, tis.Name)
			tracks  gortsplib.Tracks
		)

		if "video/VP8" == remoteTrack.Codec().MimeType {
			tracks = append(tracks, &gortsplib.TrackVP8{
				PayloadType: uint8(remoteTrack.PayloadType()),
			})
		}

		if len(tracks) > 0 {
			if err = cli.StartPublishing(rtspURL, tracks); err != nil {
				log.Println(err)
			} else {
				cliOK = true
				log.Printf("rtsp push ok. %v", rtspURL)
			}
		}
	}

	pkt := &rtp.Packet{}
	rtpBuf := make([]byte, 1500)
	for {
		n, _, readErr := remoteTrack.Read(rtpBuf)
		if readErr != nil {
			log.Println(readErr)
			break
		}

		if true {
			var receiverConnections []*WeSocketWebrtcConnectionPull
			tis.receiverMux.RLock()
			for _, connection := range tis.receiver {
				receiverConnections = append(receiverConnections, connection)
			}
			tis.receiverMux.RUnlock()

			for _, connection := range receiverConnections {
				_, _ = connection.Write(remoteTrack, rtpBuf[:n])
				// _ = connection.WriteRTP(remoteTrack, pkt)
			}
		}

		if true {
			if err = pkt.Unmarshal(rtpBuf[:n]); err != nil {
				log.Println(err)
				break
			}

			if cliOK {
				if err = cli.WritePacketRTP(0, pkt, true); err != nil {
					log.Println(err)
					break
				}
			}
		}

		// log.Printf("recv packet [%v] %v %v", remoteTrack.Kind(), pkt.SequenceNumber, len(pkt.Payload))
	}
}

// NewTrackerRTP 创建接收流
func (tis *WeSocketWebrtcConnectionPush) NewTrackerRTP(kinds []webrtc.RTPCodecType) []*webrtc.TrackLocalStaticRTP {
	var rtpTrackerList []*webrtc.TrackLocalStaticRTP

	for _, receiver := range tis.pc.GetReceivers() {
		for _, tracker := range receiver.Tracks() {
			for _, kind := range kinds {
				if tracker.Kind() == kind {
					t, err := webrtc.NewTrackLocalStaticRTP(tracker.Codec().RTPCodecCapability, tracker.Kind().String(), "pion")
					if err != nil {
						log.Printf("NewTrackLocalStaticRTP fail. %v", err)
						break
					}

					rtpTrackerList = append(rtpTrackerList, t)
				}
			}
		}
	}

	return rtpTrackerList
}
